package com.ian.controller;

import com.ian.annotation.CurrentUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.Callable;

@RestController
public class TestController {

    @GetMapping("/hello")
    public String hello() {
        System.out.println("hello");
        return "hello";
    }

    @GetMapping("/hello1")
    public String hello1() {
        System.out.println("hello1");
        return "hello1";
    }

    @GetMapping("/hello2")
    public String hello2() {
        System.out.println("hello2");
        return "hello2";
    }

    @GetMapping("/user")
    public String user() {
        System.out.println("user");
        return "user";
    }

    @GetMapping("/admin")
    public String admin() {
        System.out.println("admin");
        return "admin";
    }

    /**
     * Once AuthenticationPrincipalArgumentResolver is properly configured, you can be entirely decoupled from Spring Security in your Spring MVC layer.
     * Consider a situation where a custom UserDetailsService that returns an Object that implements UserDetails and your own CurrentUser Object. The CurrentUser of the currently authenticated user could be accessed using the following code
     *
     * 官方文档中的例子是根据当前登录用户查询其收件箱的消息
     * 因为比较麻烦，目前仅仅是查找出当前登录用户的身份（Principal），也是达到的目的了。
     *
     *
     * @return String
     */
    @GetMapping("/messages/inbox")
    public String findMessagesForUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserDetails userDetails = authentication == null ? null : (UserDetails) authentication.getPrincipal();
        // .. find messages for this user and return them ...
        return userDetails.toString();
    }

    /**
     * As of Spring Security 3.2 we can resolve the argument more directly by adding an annotation
     *
     * 官方文档中的例子是根据当前登录用户查询其收件箱的消息
     * 因为比较麻烦，目前仅仅是查找出当前登录用户的身份（Principal），也是达到的目的了
     *
     * @return String
     */
    @GetMapping("/messages/inbox2")
    public String findMessagesForUser2(@AuthenticationPrincipal UserDetails userDetails) {
        // .. find messages for this user and return them ...
        return userDetails.toString();
    }

    /**
     * We can further remove our dependency on Spring Security by making @AuthenticationPrincipal a meta annotation on our own annotation. Below we demonstrate how we could do this on an annotation named @CurrentUser.
     *
     * @param userDetails
     * @return
     */
    @GetMapping("/messages/inbox3")
    public String findMessagesForUser3(@CurrentUser UserDetails userDetails) {
        // .. find messages for this user and return them ...
        return userDetails.toString();
    }

    /**
     * Spring Web MVC 3.2+ has excellent support for Asynchronous Request Processing. With no additional configuration, Spring Security will automatically setup the SecurityContext to the Thread that invokes a Callable returned by your controllers. For example, the following method will automatically have its Callable invoked with the SecurityContext that was available when the Callable was created:
     * More technically speaking, Spring Security integrates with WebAsyncManager. The SecurityContext that is used to process the Callable is the SecurityContext that exists on the SecurityContextHolder at the time startCallableProcessing is invoked.
     *
     * @return
     */
    @GetMapping("/processCallable")
    public Callable<String> processCallable() {

        return () -> {
            // ...
            return "processCallable";
        };
    }

    /**
     * Spring Security provides CsrfTokenArgumentResolver which can automatically resolve the current CsrfToken for Spring MVC arguments.
     *      By using @EnableWebSecurity you will automatically have this added to your Spring MVC configuration.
     *      If you use XML based configuration, you must add this yourself.
     * Once CsrfTokenArgumentResolver is properly configured, you can expose the CsrfToken to your static HTML based application.
     *
     * @param token
     * @return CsrfToken
     */
    @RequestMapping("/getCsrfToken")
    public CsrfToken csrf(CsrfToken token) {
        System.out.println(token);
        System.out.format("ParameterName: %s, HeaderName: %s, Token: %s%n", token.getParameterName(), token.getHeaderName(), token.getToken());
        return token;
    }
}
